1 : <?php
2 :
3 1 : ini_set('pcre.backtrack_limit', -1);
4 :
5 : /**
6 : * Smarty Code generator
7 : *
8 : *
9 : * @package Compiler
10 : * @author Uwe Tews
11 : */
12 :
13 : /**
14 : * Smarty Code generator
15 : *
16 : * Methods to manage code output buffer
17 : *
18 : *
19 : * @package Compiler
20 : */
21 : class Smarty_Compiler_Code extends Smarty_Exception_Magic
22 1 : {
23 :
24 : public $buffer = '';
25 : public $indentation = 0;
26 : public $savedIndentation = 0;
27 : public $indentOn = true;
28 : public $noIndent = false;
29 : public $traceback = array();
30 : public $sourceLineNo = 0;
31 : public $lastLineNo = 0;
32 : public $bufferOffset = 0;
33 : public $bufferLineNo = 0;
34 :
35 : /**
36 : * Constructor
37 : *
38 : * @param int $indentation
39 : */
40 : public function __construct($indentation = 0)
41 : {
42 935 : $this->indentation = $indentation;
43 935 : }
44 :
45 : /**
46 : * init tag code block.
47 : *
48 : * @param object $compiler compiler object
49 : * @return object the current instance
50 : */
51 : public function iniTagCode($compiler)
52 : {
53 774 : $this->buffer = '';
54 774 : $this->sourceLineNo = $compiler->lex->taglineno;
55 774 : $this->indentation = $this->savedIndentation = $compiler->template_code->indentation;
56 774 : $this->noIndent = !$compiler->suppressNocacheProcessing && $compiler->caching && ($compiler->nocache || $compiler->tag_nocache || $compiler->forceNocache);
57 :
58 774 : return $this;
59 : }
60 :
61 : /**
62 : * return tag code.
63 : *
64 : * @param object $compiler compiler object
65 : * @return string of compiled code
66 : */
67 : public function returnTagCode($compiler)
68 : {
69 769 : $compiler->template_code->indentation = $this->indentation;
70 769 : $compiler->template_code->savedIndentation = $this->savedIndentation;
71 :
72 769 : return $this;
73 : }
74 :
75 : /**
76 : * Adds source line number
77 : * @param int $lineNo source line number
78 : * @return Smarty_Compiler_code
79 : */
80 : public function addSourceLineNo($lineNo)
81 : {
82 915 : if ($lineNo != $this->lastLineNo) {
83 915 : $this->updateBufferInfo();
84 915 : $this->php("// line {$lineNo}")->newline();
85 915 : $this->traceback[$this->bufferLineNo] = $lineNo;
86 915 : $this->lastLineNo = $lineNo;
87 915 : }
88 :
89 915 : return $this;
90 : }
91 :
92 : /**
93 : * Merge trackeback
94 : * @param array $traceback
95 : * @return Smarty_Compiler_code
96 : */
97 : public function mergeTraceBackInfo($traceback)
98 : {
99 879 : $this->updateBufferInfo();
100 879 : foreach ($traceback as $bufferLineNo => $lineNo) {
101 846 : $this->traceback[$this->bufferLineNo + $bufferLineNo] = $lineNo;
102 846 : $this->lastLineNo = $lineNo;
103 879 : }
104 :
105 879 : return $this;
106 : }
107 :
108 : /**
109 : * Merge other code buffer into current
110 : * @param Smarty_Compiler_Code $code
111 : * @return Smarty_Compiler_code
112 : */
113 : public function mergeCode($code)
114 : {
115 883 : if ($code->sourceLineNo != 0) {
116 763 : $this->addSourceLineNo($code->sourceLineNo);
117 763 : }
118 879 : $this->mergeTraceBackInfo($code->traceback);
119 879 : $this->raw($code->buffer);
120 879 : return $this;
121 : }
122 :
123 : /**
124 : * Update buffer line number and offset
125 : * @param int $lineNo source line number
126 : * @return Smarty_Compiler_code
127 : */
128 : public function updateBufferInfo() {
129 : // when mbstring.func_overload is set to 2
130 : // mb_substr_count() replaces substr_count()
131 : // but they have different signatures!
132 935 : if (ini_get('mbstring.func_overload') & 2) {
133 0 : $this->bufferLineNo += mb_substr_count(mb_substr($this->buffer, $this->bufferOffset), "\n");
134 0 : } else {
135 935 : $this->bufferLineNo += substr_count($this->buffer, "\n", $this->bufferOffset);
136 : }
137 935 : $this->bufferOffset = strlen($this->buffer);
138 935 : return $this;
139 : }
140 :
141 : /**
142 : * Enable indentation
143 : *
144 : *
145 : * @return object the current instance
146 : */
147 : public function indentOn()
148 : {
149 0 : $this->indentOn = true;
150 :
151 0 : return $this;
152 : }
153 :
154 : /**
155 : * Enable indentation
156 : *
157 : *
158 : * @return object the current instance
159 : */
160 : public function indent_off()
161 : {
162 0 : $this->indentOn = false;
163 :
164 0 : return $this;
165 : }
166 :
167 : /**
168 : * Adds a raw string to the compiled code.
169 : *
170 : * @param string $string The string
171 : * @return object the current instance
172 : */
173 : public function raw($string)
174 : {
175 901 : $this->buffer .= $string;
176 :
177 901 : return $this;
178 : }
179 :
180 : /**
181 : * Add an indentation to the current buffer.
182 : *
183 : * @return object the current instance
184 : */
185 : public function addIndentation()
186 : {
187 948 : if ($this->indentOn && !$this->noIndent) {
188 948 : $this->buffer .= str_repeat(' ', $this->indentation * 4);
189 948 : }
190 :
191 948 : return $this;
192 : }
193 :
194 : /**
195 : * Add newline to the current buffer.
196 : *
197 : * @return object the current instance
198 : */
199 : public function newline()
200 : {
201 948 : if (!$this->noIndent) {
202 948 : $this->buffer .= "\n";
203 948 : }
204 :
205 948 : return $this;
206 : }
207 :
208 : /**
209 : * Add a line of PHP code to output.
210 : *
211 : * @param string $value PHP source
212 : * @return object the current instance
213 : */
214 : public function php($value)
215 : {
216 948 : $this->addIndentation();
217 948 : $this->buffer .= $value;
218 :
219 948 : return $this;
220 : }
221 :
222 : /**
223 : * Adds a quoted string to the compiled code.
224 : *
225 : * @param string $value The string
226 : * @param bool $double_quote flag if double quotes shall be used
227 : * @return object the current instance
228 : */
229 : public function string($value, $double_quote = true)
230 : {
231 609 : $length = strlen($value);
232 609 : if ($length <= 1000) {
233 609 : if ($double_quote) {
234 416 : $this->buffer .= sprintf('"%s"', addcslashes($value, "\0\n\r\t\"\$\\"));
235 416 : } else {
236 411 : $this->buffer .= "'" . $value ."'";
237 : }
238 609 : } else {
239 0 : $i = 0;
240 0 : while (true) {
241 0 : if ($double_quote) {
242 0 : $this->buffer .= sprintf('"%s"', addcslashes(substr($value, $i, 1000), "\0\n\r\t\"\$\\"));
243 0 : } else {
244 0 : $this->buffer .= "'" . substr($value, $i, 1000) . "'";
245 : }
246 0 : if ($i == 0) {
247 0 : $this->indent();
248 0 : }
249 0 : $i += 1000;
250 0 : if ($i >= $length) {
251 0 : $this->outdent();
252 0 : break;
253 : }
254 0 : $this->raw("\n")->addIndentation()->raw(', ');
255 0 : }
256 : }
257 :
258 609 : return $this;
259 : }
260 :
261 : /**
262 : * Adds the PHP representation of a given value to the current buffer
263 : *
264 : * @param mixed $value The value to convert
265 : * @param bool $double_qoute flag to use double quotes on strings
266 : * @return object the current instance
267 : */
268 : public function repr($value, $double_qoute = true)
269 : {
270 879 : if (is_int($value) || is_float($value)) {
271 877 : if (false !== $locale = setlocale(LC_NUMERIC, 0)) {
272 877 : setlocale(LC_NUMERIC, 'C');
273 877 : }
274 :
275 877 : $this->raw($value);
276 :
277 877 : if (false !== $locale) {
278 877 : setlocale(LC_NUMERIC, $locale);
279 877 : }
280 879 : } elseif (null === $value) {
281 0 : $this->raw('null');
282 879 : } elseif (is_bool($value)) {
283 33 : $this->raw($value ? 'true' : 'false');
284 879 : } elseif (is_array($value)) {
285 879 : $this->raw("array(\n")->indent(2)->addIndentation();
286 879 : $i = 0;
287 879 : foreach ($value as $key => $val) {
288 879 : if ($i++) {
289 239 : $this->raw(",\n")->addIndentation();
290 239 : }
291 879 : $this->repr($key, $double_qoute);
292 879 : $this->raw(' => ');
293 879 : $this->repr($val, $double_qoute);
294 879 : }
295 879 : $this->outdent()->raw("\n")->addIndentation()->raw(')')->outdent();
296 879 : } else {
297 411 : $this->string($value, $double_qoute);
298 : }
299 :
300 879 : return $this;
301 : }
302 :
303 : /**
304 : * Indents the generated code.
305 : *
306 : * @param integer $step The number of indentation to add
307 : * @return object the current instance
308 : */
309 : public function indent($step = 1)
310 : {
311 883 : $this->indentation += $step;
312 :
313 883 : return $this;
314 : }
315 :
316 : /**
317 : * Outdents the generated code.
318 : *
319 : * @param integer $step The number of indentation to remove
320 : *
321 : * @throws Smarty_Exception
322 : * @return object the current instance
323 : */
324 : public function outdent($step = 1)
325 : {
326 : // can't outdent by more steps that the current indentation level
327 882 : if ($this->indentation < $step) {
328 0 : throw new Smarty_Exception('Unable to call outdent() as the indentation would become negative');
329 : }
330 882 : $this->indentation -= $step;
331 :
332 882 : return $this;
333 : }
334 :
335 : /**
336 : * Format and add aPHP code block to current buffer.
337 : *
338 : * @param string $value PHP source to format
339 : * @return object the current instance
340 : */
341 : public function formatPHP($value)
342 : {
343 38 : $save = $this->indentOn;
344 38 : $this->indentOn = true;
345 38 : preg_replace_callback('%(\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'|"[^"\\\\]*(?:\\\\.[^"\\\\]*)*")|([\r\n\t ]*(\?>|<\?php)[\r\n\t ]*)|(;[\r\n\t ]*)|({[\r\n\t ]*)|([\r\n\t ]*}[\r\n\t ]*)|([\r\n\t ]*)|([\r\n\t ]*// line (\d*)[\r\n\t ]*)|(.*?(?=[\'";{}/\n]))%', array($this, '_processPHPoutput'), $value);
346 38 : $this->buffer .= "\n";
347 38 : $this->indentOn = $save;
348 :
349 38 : return $this;
350 : }
351 :
352 : /**
353 : * preg_replace callback function to process PHP output
354 : *
355 : * @param string $match match string
356 : * @return string replacement
357 : */
358 : public function _processPHPoutput($match)
359 : {
360 38 : if (empty($match[0]) || !empty($match[2])) {
361 38 : return;
362 : }
363 : // if ($this->indentOn) {
364 : // $this->raw("\n");
365 : // }
366 38 : if (!empty($match[7])) {
367 2 : return;
368 : }
369 38 : if (!empty($match[1])) {
370 26 : $this->raw($match[1]);
371 :
372 26 : return;
373 : }
374 38 : if (!empty($match[4])) {
375 38 : $this->raw(";");
376 38 : $this->indentOn = true;
377 :
378 38 : return;
379 : }
380 38 : if (!empty($match[5])) {
381 13 : $this->raw("{")->indent();
382 13 : $this->indentOn = true;
383 :
384 13 : return;
385 : }
386 38 : if (!empty($match[6])) {
387 13 : if ($this->indentOn) {
388 13 : $this->raw("\n");
389 13 : $this->indentOn = true;
390 13 : }
391 13 : $this->outdent()->addIndentation()->raw('}');
392 :
393 13 : return;
394 : }
395 38 : if (!empty($match[9])) {
396 29 : $this->addSourceLineNo($match[9]);
397 29 : return;
398 : }
399 38 : if (!empty($match[10])) {
400 38 : if ($this->indentOn) {
401 38 : $this->addIndentation();
402 38 : }
403 38 : $this->raw($match[10]);
404 38 : $this->indentOn = false;
405 :
406 38 : return;
407 : }
408 :
409 0 : return;
410 : }
411 : }
|